// ==================================================================================
// Shared Genomics Project MPI Codebase
// Version 1.0 30/04/2010
//
// (c) 2010 University of Manchester all rights reserved
//
// This file is distributed under the GNU General Public License, Version 2.  
// Please see the file COPYING.txt for more details
// ==================================================================================

/*!
\file 
\brief Main Body of PFORMAT_DATA2, parrallelised filtering program
*/

/*! 
\defgroup	pformat_data2 PFORMAT_DATA2 - parallel Genomic Data-set Filter
\ingroup    mpi_stats
\brief		PFORMAT_DATA2 - parallel Genomic Data-set Filter
\details
<p>This application provide basic filtering for allelic data and is based on the PLINK filtering functions.
It always applies the standard filters of missingness (SNP or Person) and MAF using default threshold or
settings from command-line parameters.
The Application of the Hardy-Weinberg Filter is dependent upon specifying the <em>hwe</em> parameter.</p>

<p>This program also generates randomisation artifacts for the processor cores on a computer cluster.</p>

<h2>OPTIONS</H2>

<table class=options border=0>
<tr><th class=param_header colspan=1>PARAMETER</th>
<th class=param_header colspan=3>DESCRIPTION</th></tr>

<tr><td class=param_name colspan=1>--id STRING</td>
<td class=param_desc colspan=3>Unique identifer for a cluster job (also sets output file stem).</td></tr>

<tr><td class=param_name colspan=1>--rdir PATH</td>
<td class=param_desc colspan=3>Path to remote working directory on central file system.</td></tr>

<tr><td class=param_name colspan=1>--ldir PATH</td>
<td class=param_desc colspan=3>Path to local working directory on each core of the cluster.</td></tr>

<tr><td class=param_name colspan=1>--bmap FILE</td>
<td class=param_desc colspan=3>Path to the BMAP file (binary-encoded SNP list).</td></tr>

<tr><td class=param_name colspan=1>--bped FILE</td>
<td class=param_desc colspan=3>Path to the BPED file (binary-encoded pedigree data).</td></tr>

<tr><td class=param_name colspan=1>--phe FILE</td>
<td class=param_desc colspan=3>
Path to  text file containing the phenotype data. This file accessed by index or by named columns.
Missing phenotype values labelled as -9.
</td></tr>

<tr><td class=param_name colspan=1>--mpheno 1..n</td>
<td class=param_desc colspan=3>Phenotype column index to read from text file. Indices start from '1' (cf. PLINK)</td></tr>

<tr><td class=param_name colspan=1>--pheno-name STRING</td>
<td class=param_desc colspan=3>A named column to read from the phenotype text file.</td></tr>

<tr><td class=param_name colspan=1>--hwe 0.0..1.0</td>
<td class=param_desc colspan=3>Apply Hardy-Weinberg %filter and specify the threshold</td></tr>

<tr><td class=param_name colspan=1>--mind 0.0..1.0</td>
<td class=param_desc colspan=3>Set the threshold person missingness %filter</td></tr>

<tr><td class=param_name colspan=1>--geno 0.0..1.0</td>
<td class=param_desc colspan=3>Set the threshold SNP missingness %filter</td></tr>

<tr><td class=param_name colspan=1>--maf 0.0..1.0</td>
<td class=param_desc colspan=3>Set the threshold Minor allelic frequency (MAF) %filter </td></tr>

<tr><td class=param_name colspan=1>--available-procs 1..n</td>
<td class=param_desc colspan=3>Set number of processor cores in the computer cluster</td></tr>

<tr><td class=param_name colspan=1>--row-major i or s </td>
<td class=param_desc colspan=3>Set the row-major of the filtered data-set. 
Either <em>i</em> for Individual-major or <em>s</em> for SNP-major.
</td></tr>

<tr><td class=param_name colspan=1>--nperm 1..10000 </td>
<td class=param_desc colspan=3>Set number of permutations if need to generate permutation index file to control
%sample randomisation.
</td></tr>

<tr><td class=param_name colspan=1>--seed NUMBER</td>
<td class=param_desc colspan=3>Set the seed value for the PRNG. 
If this value not specified at the command-line, defaults to the current system clock.
</td></tr>

<tr><td class=param_name colspan=1>--exclude-list FILE</td>
<td class=param_desc colspan=3>Path to text file containing a person exclusion list, i.e. people
to remove from a data-set. List simply indices of people contained in the %sample array.
Text formatted, 1 number per line.
</td></tr>

<tr><td class=param_name colspan=1>--missingness FILE</td>
<td class=param_desc colspan=3>Path to pre-calculated person missingness file.
</td></tr>

<tr><td class=param_name colspan=1>--founders FILE</td>
<td class=param_desc colspan=3>Path to founders file containing gender data of people in the %sample array.
</td></tr>

<tr><td class=param_name colspan=1>--no-rng</td>
<td class=param_desc colspan=3>If state flag set, suppresses the creation of randomisation artifacts like the seed file.
</td></tr>
</table>
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>

#include "SMG_All.h"

/*!
\brief Command-line options of PFORMAT_DATA2
*/
struct option long_options[] = {
	{ "bmap", required_argument, NULL, 'm' }, // Path to the BMAP file.
	{ "bped", required_argument, NULL, 'B' }, // Path to BPED File.
	{ "phe", required_argument, NULL, 'f' }, // PHE phenotype file.
	{ "id", required_argument, NULL, 'i' }, // Run ID
	{ "ldir", required_argument, NULL, 'd'}, // Output direct
	{ "rdir", required_argument, NULL, 'R'}, // Output direct
	{ "mpheno", required_argument, NULL, 'n'}, // Index number of data column in the PHE file.
	{ "hwe", required_argument, NULL, 'a'}, // Default Hardy-Weinburg filter
	{ "hwe2", required_argument, NULL, 'b'}, // The Other Hardy-Weinburg filter
	{ "mind", required_argument, NULL, 'e'}, // Missingness per individual.
	{ "geno", required_argument, NULL, 'g'}, // Missingness per marker
	{ "maf", required_argument, NULL, 'h'}, // Minor Allele filter_statistics_localuency
	{ "available-procs", required_argument, NULL, 'N'}, // Number processors in cluster, used to size the random seed file.
	{ "row-major", required_argument, NULL, 'r'}, // Row major of PED file (SNP or individual).
	{ "nperm", required_argument, NULL, 'W'}, // No. of permutations
	{ "seed", required_argument, NULL, 'S' }, // Seed value for the random number generator.
	{ "mode", required_argument, NULL, 'M' }, // mode to run this programme.
	{ "exclude-list", required_argument, NULL, 'E'}, // Sample exclusion list a.k.a category filter.
	{ "missingness", required_argument, NULL, 'Q'}, // Path to sample missingness file.
	{ "founders", required_argument, NULL, 'F'}, // Path to founders/gender data file.
	{ "no-rng", optional_argument, NULL, 'z' },  // Suppress the generation of the RNG files (compatibility flag for solcol.exe).
	{ "pheno-name", required_argument, NULL, 'Z' },  // pheno column name.
	{ "preserve", optional_argument, NULL, 'p'}, // Preserve all genotypes (for use when transposing the data-set).
	{ 0,    0,    0,    0   } 
};

struct small_locus *map = NULL;
struct chromosomes _chromos;
struct pedfile _p;
struct mapfile _m;
double *_phe_values = NULL, *missingness_per_person = NULL, *filter_statistics_local = NULL, *filter_statistics_global = NULL;
BOOL *founders_data = NULL, *_persons_to_del = NULL, *snps_to_del_local = NULL, *phe_missing = NULL, *snps_to_del_global = NULL;
char *_bpedData = NULL, *reasons_to_del_local = NULL, *reasons_to_del_global = NULL;

void cleanUp() {
	// Free-up the dynamic memory.
	chromosomes_free_attributes(&_chromos);
	if (map != NULL) { free(map); map = NULL; }
	if (_phe_values != NULL) { free(_phe_values); _phe_values = NULL; }
	if (_bpedData != NULL) { free(_bpedData); _bpedData = NULL; }
	if (missingness_per_person != NULL) { free(missingness_per_person); missingness_per_person = NULL; }
	if (founders_data != NULL) { free(founders_data); founders_data = NULL; }
	if (_persons_to_del != NULL) { free(_persons_to_del); _persons_to_del = NULL; }
	if (snps_to_del_local != NULL) { free(snps_to_del_local); snps_to_del_local = NULL; }
	if (reasons_to_del_local != NULL) { free(reasons_to_del_local); reasons_to_del_local = NULL; }
	if (filter_statistics_local != NULL) { free(filter_statistics_local); filter_statistics_local = NULL; }
	if (phe_missing != NULL) { free(phe_missing); phe_missing = NULL; }
	if (snps_to_del_global != NULL) { free(snps_to_del_global); snps_to_del_global = NULL; }
	if (filter_statistics_global != NULL) { free(filter_statistics_global); filter_statistics_global = NULL; }
	if (reasons_to_del_global != NULL) { free(reasons_to_del_global); reasons_to_del_global = NULL; }
	pedfile_free_attributes(&_p);
	mapfile_free_attributes(&_m);
}

#define FILTER_MODE 1
#define WRITE_MODE 2
#define CANCEL_MODE 3
#define NREQUIRED_ARGS 9

#define MAX_LINE_LENGTH 200000
#define MAX_TOKEN_LENGTH 80

/*!
\brief Main body of the PFORMAT_DATA2 program
\ingroup pformat_data2
*/
int pformat_main(int argc, char *argv[]) {
	char ch;
	struct selected_options ops;
	int nRequiredArgs = NREQUIRED_ARGS, mode = FILTER_MODE, nAvailableProcs = AVAILABLE_CORES;
	BOOL isSnpMajor = FALSE, isIndividualMajor = FALSE, jobDone = FALSE, hasExcludeList = FALSE, allocated_globals = FALSE, do_rng = TRUE;
	int myrank = 0, numprocs = 1, result;
	char rowmode = 's';
	unsigned long seed = (unsigned long) time(NULL); 
	int nSNPs = 0, _nSNPs_all = 0, nSamples = 0, allocatedAllRequiredBuffers = 0, _allocatedAllRequiredBuffers = 0, wrote_bped_fragment = 0, _wrote_bped_fragment = 0;

	// Initialise MPI environment, get size and rank.
	if ((result = MPI_Init(&argc,&argv)) != MPI_SUCCESS) mpi_error(result, "Initialisation");
	if ((result = MPI_Comm_size(MPI_COMM_WORLD,&numprocs)) != MPI_SUCCESS) mpi_error(result, "Number of processors");
	if ((result = MPI_Comm_rank(MPI_COMM_WORLD,&myrank)) != MPI_SUCCESS) mpi_error(result, "Processor number");

	// Initialise some stuff.
	options_init(&ops);
	pedfile_init(&_p);
	mapfile_init(&_m);
	chromosomes_init(&_chromos);
	chromosomes_define_human(&_chromos);
	*ops.ldir = *ops.rdir = '\0' ; 
	ops.verbose = FALSE;

	if (argc == 1) {
		MPI_Finalize();
		return -1;
	} 

	while ((ch = (char) getopt_long(argc, (const char**) argv, "m:v:p:f:i:", long_options, NULL)) != -1) {
		switch (ch) {
			double value;
			unsigned long _seed;

			case 'm': // BMap File
				strcpy(ops.szBmapFileName, optarg);
				nRequiredArgs--;
				break;
			case 'B': // Binary BPED File
				strcpy(ops.szBpedFileName, optarg);
				nRequiredArgs--;
				break;
			case 'f': // Phenotype file
				strcpy(ops.szPheFileName, optarg);
				nRequiredArgs--;
				break;
			case 'i': // Run ID
				strcpy(ops.szRunId, optarg);
				break;
			case 'd': // Local working directory.
				strcpy(ops.ldir, optarg);
				nRequiredArgs--;
				break;
			case 'R': // Remote working directory.
				strcpy(ops.rdir, optarg);
				nRequiredArgs--;
				break;
			case 'n': // Column to analyse in PHE file
				ops.mpheno = atoi(optarg);
				if (ops.mpheno < 0) ops.mpheno = 1;
				break;
			case 'N': // Theoretical available processors, need to size seed file.
				nAvailableProcs = atoi(optarg);
				if (nAvailableProcs == 0) nAvailableProcs = AVAILABLE_CORES;
				break;
			case 'r': // Row-major mode for the binary formatted PED file.
				rowmode = *optarg;
				if (rowmode == 'i' || rowmode == 's') {
					if (rowmode == 's') { // SNP-major
						isSnpMajor = TRUE; 
						isIndividualMajor = FALSE;
					}  else if (rowmode == 'i') { // individual-major
						isSnpMajor = FALSE; 
						isIndividualMajor = TRUE;
					}
					nRequiredArgs--;
				}
				break;
			case 'a': // Do the HW filter on the population
				ops.HWD_test = TRUE;
				ops.HWD_standard = FALSE;
				value = atof(optarg);
				if (!(value >= 0.0 && value <= 1.0)) value = DEFAULT_HWE_LIMIT;
				ops.HWD_limit = value;
				break;
			case 'b': // Type of HW test and the filter limit.
				ops.HWD_test = TRUE;
				ops.HWD_standard = TRUE;
				value = atof(optarg);
				if (!(value >= 0.0 && value <= 1.0)) value = DEFAULT_HWE_LIMIT;
				ops.HWD_limit = value;
				break;
			case 'e': // Missing limit for Person's SNP missingness.
				value = atof(optarg);
				if (!(value >= 0.0 && value <= 1.0)) value = DEFAULT_MIND;
				ops.MAX_IND_MISSING = value;
				break;
			case 'g': // Limit for SNP missingness
				value = atof(optarg);
				if (!(value >= 0.0 && value <= 1.0)) value = DEFAULT_GENO;
				ops.MAX_GENO_MISSING = value;
				break;
			case 'h': // Major allelic Frequency.
				value = atof(optarg);
				if (!(value >= 0.0 && value <= 1.0)) value = DEFAULT_MAF;
				ops.min_af = value;
				break;
			case 'W':
				ops.nperm = atoi(optarg);
				if (ops.nperm <= 0) ops.nperm = DEFAULT_NPERM;
				break;
			case 'S':
				_seed = atol(optarg);
				if (_seed != 0) seed = _seed;
				break;
			case 'M': // Calling mode for this program (filter or right output).
				mode = atoi(optarg);
				if (mode == FILTER_MODE || mode == WRITE_MODE || mode == CANCEL_MODE) nRequiredArgs--;
				break;
			case 'E': // Sample Exclusion List
				strcpy(ops.szExcludeListFileName, optarg);
				hasExcludeList = TRUE;
				break;
			case 'Q': // path to the 'person' missingness file.
				strcpy(ops.szMissingnessFileName, optarg);
				nRequiredArgs--;
				break;
			case 'F': // path to the founders/gender file.
				strcpy(ops.szFoundersFileName, optarg);
				nRequiredArgs--;
				break;
			case 'z': // Suppress generation of the RNG file (perm index & seed files)
				do_rng = FALSE;
				break;
			case 'Z': // Pheno column-name to read from the PHE file.
				strcpy(ops.szPheColumnName, optarg);
				break;
			case 'p': // Set genotype preservation flag.
				ops.preserve_all_genotypes = TRUE;
				break;
			default:
				break;
		} 
	}
	
	if (nRequiredArgs != 0) {
		MPI_Finalize();
		return -1;
	}
	
	init_genrand(seed);
	chromosomes_define_human(&_chromos);

	if (mode == FILTER_MODE) {
		FILE *in = NULL;
		int snpsPerProcessor = -1, mpi_gather_count = 0, expected_mpi_gather_count = 3, end_snp = -1, start_snp = -1;
		
		// big I/O read so need to block before moving on.
		in = fopen(ops.szBpedFileName, "rb");
		if (in != NULL) {
			if(bpedfile_read_header(in, &nSNPs, &nSamples)) {
				snpsPerProcessor = _get_tasks_per_processor(numprocs, nSNPs);
				start_snp = snpsPerProcessor * myrank;
				end_snp = start_snp + snpsPerProcessor;
				if (end_snp > nSNPs || myrank == numprocs - 1) end_snp = nSNPs;
				_bpedData = bpedfile_fseek(in, nSNPs, nSamples, start_snp, end_snp);
				_nSNPs_all = nSNPs;
				nSNPs = abs(end_snp - start_snp);
			}
			fclose(in);
		}
		if (_bpedData != NULL) {
			// Allocate the result arrays associated with the filtering.
			_persons_to_del = create_bool_array(nSamples, FALSE);
			phe_missing = create_bool_array(nSamples, FALSE);
			snps_to_del_local = create_bool_array(snpsPerProcessor, FALSE);
			reasons_to_del_local = create_char_array(snpsPerProcessor, FILTER_REASON_NOTHING);
			filter_statistics_local = create_vector(snpsPerProcessor, -1);
			missingness_per_person = missingnessfile_load(&ops, &nSamples);
			founders_data = foundersfile_load(&ops, &nSamples);
			map = bmapfile_fseek(&ops, start_snp, end_snp);
			_phe_values = phefile_load(&ops, nSamples, phe_missing);

			if (myrank == ROOT) {
				size_t space_to_alloc = snpsPerProcessor * numprocs;
				snps_to_del_global = (BOOL*) malloc(sizeof(BOOL) * space_to_alloc); 
				filter_statistics_global = (double*) malloc(sizeof(double) * space_to_alloc);
				reasons_to_del_global = (char*) malloc(sizeof(char) * space_to_alloc); 

				if (snps_to_del_global != NULL && filter_statistics_global != NULL && reasons_to_del_global != NULL) allocated_globals = TRUE;
			} else allocated_globals = TRUE;
		}
		if (MPI_Barrier(MPI_COMM_WORLD) != MPI_SUCCESS) {
			cleanUp();
			MPI_Finalize();
			return -1;
		}
		
		if (_bpedData != NULL && _persons_to_del != NULL && snps_to_del_local != NULL && reasons_to_del_local != NULL &&
			filter_statistics_local != NULL && phe_missing != NULL && missingness_per_person != NULL && map != NULL && _phe_values != NULL) allocatedAllRequiredBuffers = 1;

		if(MPI_Allreduce(&allocatedAllRequiredBuffers, &_allocatedAllRequiredBuffers, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD) != MPI_SUCCESS) {
			cleanUp();
			MPI_Finalize();
			return -1;
		}
		
		if (_allocatedAllRequiredBuffers != numprocs) {
			cleanUp();
			MPI_Finalize();
			return -1;
		} else {
			BOOL *sex = founders_data, *founders = founders_data + nSamples; // Set the gender & founder pointers as in the same array.
			if (hasExcludeList) bfilter_parse_exclude_list(&ops, nSamples, _persons_to_del); // Category filter.
			bfilter_person_do_mind(nSamples, missingness_per_person, _persons_to_del, &ops);
			bfilter_snp_do_maf_and_mind(_bpedData, map, nSNPs, snps_to_del_local, reasons_to_del_local, filter_statistics_local, nSamples, _persons_to_del, sex, founders, &_chromos, &ops);
			if (ops.HWD_test) bfilter_snp_do_hwe(_bpedData, map, nSNPs, snps_to_del_local, reasons_to_del_local, filter_statistics_local, nSamples, _persons_to_del, sex, founders, _phe_values, &_chromos, &ops);
			
			// If the BPED write fails there's really no-point carrying on from here.
			if (bpedfile_write_filtered(_bpedData, nSNPs, nSamples, snps_to_del_local, _persons_to_del, myrank, &ops, ops.ldir, ops.rdir)) wrote_bped_fragment = 1;
		}
		if (MPI_Barrier(MPI_COMM_WORLD) != MPI_SUCCESS) {
			cleanUp();
			MPI_Finalize();
			return -1;
		}
		if(MPI_Allreduce(&wrote_bped_fragment, &_wrote_bped_fragment, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD) != MPI_SUCCESS) {
			cleanUp();
			MPI_Finalize();
			return -1;
		}
		if (_wrote_bped_fragment != numprocs) {
			cleanUp();
			MPI_Finalize();
			return -1;
		}
		if (MPI_Gather(snps_to_del_local, snpsPerProcessor, MPI_UNSIGNED_CHAR, snps_to_del_global, snpsPerProcessor, MPI_UNSIGNED_CHAR, ROOT, MPI_COMM_WORLD) == MPI_SUCCESS)  { 
			mpi_gather_count++;
			if (MPI_Gather(reasons_to_del_local, snpsPerProcessor, MPI_CHAR, reasons_to_del_global, snpsPerProcessor, MPI_CHAR, ROOT, MPI_COMM_WORLD) == MPI_SUCCESS)  {
				mpi_gather_count++;
				if (MPI_Gather(filter_statistics_local, snpsPerProcessor, MPI_DOUBLE, filter_statistics_global, snpsPerProcessor, MPI_DOUBLE, ROOT, MPI_COMM_WORLD) == MPI_SUCCESS)  {
					mpi_gather_count++;
				}
			}
		}
		if (myrank != ROOT && mpi_gather_count == expected_mpi_gather_count) jobDone = TRUE;
		else if (myrank == ROOT && mpi_gather_count == expected_mpi_gather_count) {
			if (boolfile_create_and_copy(snps_to_del_global, _nSNPs_all, &ops, SUFFIX_SNP_TO_DEL) && 
				boolfile_create_and_copy(_persons_to_del, nSamples, &ops, SUFFIX_PERSON_TO_DEL)) 
			{
				int __SNPs = 0;
				struct small_locus* all_snps = bmapfile_load(&ops, &__SNPs);

				if (pformat_write_filter_log(_nSNPs_all, all_snps, snps_to_del_global, reasons_to_del_global, filter_statistics_global, &ops, ops.ldir, ops.rdir, nSamples, _persons_to_del, missingness_per_person)) {
					if (pformat_write_filter_dims(_nSNPs_all, nSamples, numprocs, snps_to_del_global, _persons_to_del, &ops)) {
						bfilter_write_log(&ops);
						jobDone = TRUE;
					}
				}
				if (all_snps != NULL) free(all_snps);
			}
		}
	} else if (mode == WRITE_MODE && numprocs == 1) { // Single core operation, multi-core here would not be a good idea.
		BOOL hasBinaryPed = FALSE, hasSampleFile = FALSE, hasSeedFile = FALSE, hasBmapFile = FALSE, doneMainSetup = FALSE;
		
		// Load in the inclusion buffers & phenotype data.
		snps_to_del_global = boolfile_read(&ops, SUFFIX_SNP_TO_DEL, &nSNPs);
		_persons_to_del = boolfile_read(&ops, SUFFIX_PERSON_TO_DEL, &nSamples);
		phe_missing = create_bool_array(nSamples, FALSE);
		_phe_values = phefile_load(&ops, nSamples, phe_missing);
		map = bmapfile_load(&ops, &nSNPs);
		founders_data = foundersfile_load(&ops, &nSamples);

		if (_phe_values != NULL && snps_to_del_global != NULL && phe_missing != NULL && snps_to_del_global != NULL && map != NULL && founders_data != NULL) {
			if(bmapfile_write_filtered(map, nSNPs, snps_to_del_global, &ops, ops.ldir, ops.rdir)) {
				hasBmapFile = TRUE;
				if(pformat_alloc_samples(&_p, nSamples, _phe_values, _persons_to_del, phe_missing, founders_data)) {
					if(pformat_read_fbped_fragments(&ops, &_m)) doneMainSetup = TRUE;
				}
			}
		}
		
		// Write out the rest of the input files for the cluster.
		if (doneMainSetup) {
			// Create the Binary PED file.
			if (isIndividualMajor) {
				if (snp_to_ind(&_m, &_p)) hasBinaryPed = ipedfile_create_and_copy(&ops, &_p, myrank) ? TRUE : FALSE;
			} else if (isSnpMajor) hasBinaryPed = spedfile_create_and_copy(&ops, &_m, myrank) ? TRUE : FALSE;
	
			// Create the sample file.
			if (hasBinaryPed) hasSampleFile = samplefile_create_and_copy(&ops, &_p, myrank) ? TRUE : FALSE;
			
			// Create seed file for the PRNG for each cluster rank & a randomised index file
			// for SNP partitioned calculations.
			if (hasBinaryPed && hasSampleFile) {
				if (do_rng) {
					if(seedfile_create_and_copy(&ops, nAvailableProcs, myrank)) 
						if (permfile_create_and_copy(&ops, ops.nperm, _p.nSample, myrank)) hasSeedFile = TRUE;
				} else 
					hasSeedFile = TRUE; 
			}

			// All flags then job was 100% successful.
			if (hasBinaryPed && hasSampleFile && hasSeedFile && hasBmapFile) { 
				jobDone = TRUE;
				if (do_rng) rseed_log(&ops, seed);
				pformat_cleanup(&ops);
			}
		}
	} else if (mode == CANCEL_MODE) { // Filter settings not right, => delete temporary files.
		jobDone = TRUE;
		pformat_cleanup(&ops);
	}

	cleanUp();
	MPI_Finalize();

	if (jobDone) return 0;
	else return -1;
}

int main(int argc, char *argv[]) {
	return pformat_main(argc, argv);
}